/* 存储被读取的Proxy代理对象的原始对象的属性的副作用函数集合 */
const bucket = new WeakMap();

/* 代理对象被读的属性的副作用函数 */
let activeEffect;

/* 副作用函数栈 */
const effectStack = [];

/**
 * @description 注册副作用函数
 * @param {Function} fn 副作用函数
 */
export function effect(fn, options) {
  const effectFn = () => {
    cleanup(effectFn);

    activeEffect = effectFn;

    /* 在副作用函数执行前将副作用函数压入栈中 */
    effectStack.push(effectFn);

    const res = fn();

    /* 在副作用函数执行完后将副作用函数从副作用函数栈中弹出 */
    effectStack.pop();
    /* 将当前副作用函数恢复到之前的副作用函数 */
    activeEffect = effectStack[effectStack.length - 1];

    return res;
  };

  effectFn.deps = [];

  effectFn.options = options;

  /* 只有在不是懒执行的时候才立即执行 */
  if (!options?.lazy) {
    effectFn();
  }

  return effectFn;
}

/**
 * @description 代理对象被读的属性的副作用函数集合
 * @param {Object} target 代理对象的源对象
 * @param {String} key 被读取的代理的源对象中的属性
 */
export function track(target, key, shouldTrack = true) {
  // console.log("track执行");
  if (!activeEffect || !shouldTrack) return;

  let depsMap = bucket.get(target);

  if (!depsMap) {
    depsMap = new Map();
    bucket.set(target, depsMap);
  }

  let deps = depsMap[key];

  if (!deps) {
    deps = new Set();
    depsMap.set(key, deps);
  }

  deps.add(activeEffect);
  activeEffect.deps.push(deps);
  // console.log(activeEffect.deps);
}

/**
 * @description 触发副作用函数
 * @param {Object} target 代理对象的源对象
 * @param {String} key 被设置的代理的源对象中的属性
 * @returns
 */
export function trigger(
  target,
  { key, ITERATE_KEY, MAP_KEY_ITERATE_KEY },
  type,
  newValue
) {
  const depsMap = bucket.get(target);

  if (!depsMap) return;

  const effects = depsMap.get(key);

  /* 防止forEach遍历Set时无限循环（因为副作用函数删了之后又在读取的时候被添加了） */
  const effectsToRun = new Set();

  /* 避免当一个副作用函数中同时出现读取和设置响应式数据无限递归执行副作用函数，导致栈溢出 */
  effects &&
    effects.forEach((effectFn) => {
      if (effectFn !== activeEffect) {
        effectsToRun.add(effectFn);
      }
    });

  /* for循环遍历，取出ITERATE_KEY相关的副作用函数并执行 */
  if (
    type === "ADD" ||
    type === "DELETE" ||
    (type === "SET" &&
      Object.prototype.toString.call(target) === "[object Map]")
  ) {
    const iterateEffects = depsMap.get(ITERATE_KEY);

    iterateEffects &&
      iterateEffects.forEach((iterateEffectFn) => {
        if (iterateEffectFn !== activeEffect) {
          effectsToRun.add(iterateEffectFn);
        }
      });
  }

  /* 当Map类型的代理对象调用keys进行for...of循环时，取出与MAP_KEY_ITERATE_KEY相关的副作用函数并执行 */
  if (
    (type === "ADD" || type === "DELETE") &&
    Object.prototype.toString.call(target) === "[object Map]"
  ) {
    const iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY);

    iterateEffects &&
      iterateEffects.forEach((iterateEffectFn) => {
        if (iterateEffectFn !== activeEffect) {
          effectsToRun.add(iterateEffectFn);
        }
      });
  }

  /* 当操作类型为ADD，并且代理的是数组时，应该取出并执行 length 属性相关的副作用函数 */
  if (Array.isArray(target) && type === "ADD") {
    const lengthEffects = depsMap.get("length");

    lengthEffects &&
      lengthEffects.forEach((lengthEffectFn) => {
        if (lengthEffectFn !== activeEffect) {
          effectsToRun.add(lengthEffectFn);
        }
      });
  }

  /* 如果代理的是数组，并且修改的是数组的length属性 */
  if (Array.isArray(target) && key === "length") {
    /* 遍历属性与副作用函数关系映射表 */
    depsMap.forEach((deps, key) => {
      /* 对于索引大于等于length属性的新的值的（新的数组长度） */
      if (key >= newValue) {
        /* 将其所关联的所有副作用函数取出并添加到 待执行的集合中去 */
        deps.forEach((effectFn) => {
          if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn);
          }
        });
      }
    });
  }

  /* 执行当前被设置的属性的所有副作用函数 */
  effectsToRun.forEach((effectFn) => {
    if (effectFn.options?.scheduler) {
      /* 调度执行：由用户决定在什么时候以什么方式执行 */
      effectFn.options.scheduler(effectFn);
    } else {
      effectFn();
    }
  });
}

/**
 * @description 清理副作用函数
 * @param {Function} effectFn 副作用函数
 */
function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];

    deps.delete(effectFn);
  }

  effectFn.deps.length = 0;
}
